package grid;
import java.util.ArrayList;

public class FlowerGrid {
	private Point[][] grid;
	private int[][] bound;
	private int radius;
	private int maxDist;
	/**
	 * Constructs a default FlowerGrid with size 3
	 */
	public FlowerGrid() {
		this(3);
	}
	/**
	 * Constructs a flower grid of a specified radius.
	 * @param radius How many rings it has (1 ring is just one square)
	 */
	public FlowerGrid(int radius) {
		this.radius = radius;
		maxDist = (2*radius)-1;
		grid = new Point[maxDist*2+1][maxDist*2+1];
		populateBoundary();
	}
	/**
	 * Here be dragons. Figures out the maximum/minimum y values of the grid for each row
	 * and stores them in a bound array, with bound[y][0] representing the minimum x value
	 * and bound[y][1] representing the maximum x value for a specific y value.
	 */
	private void populateBoundary() {
		bound = new int[maxDist+2][2];
		int i;
		int lbound = -(maxDist-1), rbound = (maxDist-1);

		for (i = 0; i < radius; i++) {
			bound[i][0] = lbound;
			bound[i][1] = rbound;

			if ((i + radius) % 2 == 0) { // if it's 0
				lbound--;
			} else {
				rbound--;
			}
		}
		for (; i <= maxDist; i++) {
			bound[i][0] = lbound;
			bound[i][1] = rbound;
			lbound += 1;
			rbound -= 2;
		}
		bound[i][0] = lbound;
		bound[i][1] = rbound;
	}
	public boolean isInGrid(Location l) {
		if (l.getY() < -(maxDist) || l.getY() > (maxDist))
			return false; // if y is too big it's obviously bad. Also want to
		// make sure no array out of bounds.
		if (l.getY() >= 0) {
			if (l.getX() >= bound[l.getY()][0] && l.getX() <= bound[l.getY()][1])
				return true;
			return false;

		}
		if (l.getX() <= -bound[-l.getY()][0] && l.getX() >= -bound[-l.getY()][1]) // if x is in
			return true;
		return false;
	}
	public Point get(Location l) {
		if(!isInGrid(l)) {
			return null;
		}
		return grid[l.getX()+maxDist][l.getY()+maxDist];
	}
	public void set(Location l,Point p) {
		if(isInGrid(l)) {
			grid[l.getX()+maxDist][l.getY()+maxDist]=p;
		}
	}
	public Location getLocation(Point e) {
		for(int x=0;x<grid.length;x++) {
			for(int y=0;y<grid[x].length;y++) {
				if(e.equals(grid[x][y])) {
					return new Location(x-maxDist,y-maxDist);
				}
			}
		}
		return null;
	}
	public boolean shouldBeResource(Location l) {
		if (!isInGrid(l))
			return false;
		Location line[] = new Location[(radius*2)-1];
		line[0] = new Location(0, 0);
		for (int i = 1; i < radius; i++) {
			line[2 * i-1] = new Location(2 * i, -i);
			line[2 * i] = new Location(-2 * i, i);
		}
		int i[] = new int[10];
		for (i[0] = 0; i[0] < line.length; i[0]++) {
			i[1] = l.getX() - line[i[0]].getX();
			i[2] = l.getY() - line[i[0]].getY();
			if (i[1] == i[2])
				return true;
		}

		return false;

	}
	/**
	 * Flattens and returns the array of elements.
	 * @return A flattened array of all of the elements present in the grid.
	 */
	public Point[] getElements() {
		Point[] ret = new Point[grid.length*grid.length];
		int i=0;
		for(int a=0;a<grid.length;a++) {
			for(int b=0;b<grid[a].length;b++) {
				if(grid[a][b]!=null) {
					ret[i] = grid[a][b];
					i++;
				}
			}
		}
		Point[] ret2 = new Point[i];
		System.arraycopy(ret,0,ret2,0,i);
		return ret2;
	}
	protected Point[] getElementsOfClass(Class<?> c) {
		Point[] elements = getElements();
		Point[] r1 = new Point[elements.length];
		int i=0;
		for(Point e : elements) {
			if(c.isAssignableFrom(e.getClass())) {
				r1[i] = e;
				i++;
			}
		}
		Point[] r2 = new Point[i-1];
		System.arraycopy(r1,0,r2,0,i-1);
		return r2;
	}
	public Resource[] getResources() {
		return (Resource[]) getElementsOfClass(Resource.class);
	}
	public Constructable[] getConstructables() {
		return (Constructable[]) getElementsOfClass(Constructable.class);
	}
}
